package com.espressif.iot.base.net.rest.mesh;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;

import com.espressif.iot.base.api.EspBaseApiUtil;
import com.espressif.iot.base.application.EspApplication;
import com.espressif.iot.base.net.mdns.MdnsDiscoverUtil;
import com.espressif.iot.base.net.udp.UdpBroadcastUtil;
import com.espressif.iot.command.device.IEspCommandDevice;
import com.espressif.iot.type.device.EspDeviceType;
import com.espressif.iot.type.net.IOTAddress;
import com.espressif.iot.util.BSSIDUtil;
import com.espressif.iot.util.InputStreamUtils;
import com.espressif.iot.util.MeshUtil;
import com.mob.tools.utils.HEX;

public class EspMeshDiscoverUtil implements IEspCommandDevice
{
    private static final Logger log = Logger.getLogger(EspMeshDiscoverUtil.class);
    
    private static final boolean IS_MDNS_ON = false;
    
    private static final int UDP_RETRY_TIME = 3;
    
    private static final String ROUTER = "router";
    
    private static final String TOPOLOGY = "topology";
    
    private static final String DEV_TYPE = "dev_type";
    
    private static Set<IOTAddress> __discoverIOTMeshDevicesOnRoot(IOTAddress rootIOTAddress, String deviceBssid)
    {
        log.error("__discoverIOTMeshDevicesOnRoot(): deviceBssid:" + deviceBssid);
        String url = "http:/" + rootIOTAddress.getInetAddress();
        String routerReq = "00000000";
        if (deviceBssid == null)
        {
            deviceBssid = "00:00:00:00:00:00";
        }
        JSONObject jsonReq = new JSONObject();
        try
        {
            jsonReq.put(TOPOLOGY, MeshUtil.getMacAddressForMesh(deviceBssid));
        }
        catch (JSONException e1)
        {
            e1.printStackTrace();
        }
        JSONArray jsonArray = EspMeshNetUtil.PostForJsonArray(url, routerReq, deviceBssid, jsonReq);
        Set<IOTAddress> iotMeshAddressSet = new HashSet<IOTAddress>();
        for (int i = 0; jsonArray != null && i < jsonArray.length(); i++)
        {
            EspDeviceType deviceType = null;
            String deviceTypeStr = null;
            try
            {
                JSONObject json = (JSONObject)jsonArray.get(i);
                String bssid = MeshUtil.getRawMacAddress(json.getString(TOPOLOGY));
                String router = json.getString(ROUTER);
                if (router.equals("00000000"))
                {
                    log.warn("router equals 00000000, it shouldn't happen");
                    continue;
                }
                if (json.has(DEV_TYPE))
                {
                    deviceTypeStr = json.getString(DEV_TYPE);
                    deviceType = EspDeviceType.getEspTypeEnumByString(deviceTypeStr);
                }
                // the root node and its children nodes has the same InetAddress.
                // the router is used to distinguish them
                InetAddress inetAddress = rootIOTAddress.getInetAddress();
                IOTAddress iotMeshAddress = new IOTAddress(bssid, inetAddress, router, true);
                // parse real device type by mesh
                if (deviceType == null)
                {
                    // the default deviceType is LIGHT
                    iotMeshAddress.setEspDeviceTypeEnum(EspDeviceType.LIGHT);
                }
                else
                {
                    iotMeshAddress.setEspDeviceTypeEnum(deviceType);
                }
                log.debug("__discoverIOTMeshDevicesOnRoot(): iotMeshAddress=[" + iotMeshAddress
                    + "] is added into iotMeshAddressSet");
                iotMeshAddressSet.add(iotMeshAddress);
            }
            catch (JSONException e)
            {
                e.printStackTrace();
            }
        }
        return iotMeshAddressSet;
    }
    
    private static Set<IOTAddress> __discoverIOTMeshDevicesOnRoot2(IOTAddress rootIOTAddress, String deviceBssid)
    {
        Set<IOTAddress> iotMeshAddressSet = new HashSet<IOTAddress>();
        InetAddress rootInetAddress = rootIOTAddress.getInetAddress();
        String rootBssid = rootIOTAddress.getBSSID();
        if (deviceBssid != null)
        {
            IOTAddress iotAddress = EspMeshNetUtil.GetTopoIOTAddress(rootInetAddress, rootBssid, deviceBssid);
            if (iotAddress != null)
            {
                iotMeshAddressSet.add(iotAddress);
            }
            return iotMeshAddressSet;
        }
        else
        {
            List<IOTAddress> iotAddressList = EspMeshNetUtil.GetTopoIOTAddressList(rootInetAddress, rootBssid);
            if (iotAddressList != null)
            {
                iotMeshAddressSet.addAll(iotAddressList);
            }
            return iotMeshAddressSet;
        }
    }
    
    // extract IOTAddress with the specific bssid
    private static Set<IOTAddress> extractIOTAddress(String bssid, IOTAddress iotAddress)
    {
        if (bssid == null)
        {
            return null;
        }
        if (iotAddress.getBSSID().equals(bssid))
        {
            Set<IOTAddress> result = new HashSet<IOTAddress>();
            result.add(iotAddress);
            return result;
        }
        return null;
    }
    
    // extract IOTAddress with the specific bssid
    private static Set<IOTAddress> extractIOTAddress(String bssid, Set<IOTAddress> iotAddressSet)
    {
        if (bssid == null)
        {
            return null;
        }
        for (IOTAddress iotAddress : iotAddressSet)
        {
            if (iotAddress.getBSSID().equals(bssid))
            {
                Set<IOTAddress> result = new HashSet<IOTAddress>();
                // don't forget to set rootDeviceBssid
                // 设置根设备未本身bssid  他不是mesh设备
                iotAddress.setRootBssid(bssid);
                result.add(iotAddress);
                return result;
            }
        }
        return null;
    }
    
    /**
     * @see IOTAddress discover IOT devices in the same AP by UDP broadcast
     * 
     * @param deviceBssid the device's bssid which is to be found, null means find all devices
     * @return
     */
    private static Set<IOTAddress> discoverIOTMeshDevices(final String deviceBssid)
    {
        // store the targetDevice set with only one element whose bssid is deviceBssid
        Set<IOTAddress> targetDeviceSet = null;
        // discover IOT Root Devices by UDP Broadcast
        final Set<IOTAddress> rootDeviceSet = new HashSet<IOTAddress>();
        
        EspSocketClient thiclent;
        //获取当前连着的路由器名称
        // if phone is connected to device, skip sending udp broadcast
        String gateway = EspApplication.sharedInstance().getGateway();
        // it has been forbidden since v0.9.7, just let the code here
        log.error("gateway's address =" + gateway);

        {
        	
            // send udp broadcast each 1000 ms no matter how much is udp broadcast SOTIMEOUT
            List<Future<?>> _futureList = new ArrayList<Future<?>>();
            for (int i = 0; i < UDP_RETRY_TIME; i++)
            {
            	//提交到线程池里面进行处理  完成之后返回到_future，罪过提交3次请求
                Future<?> _future = EspBaseApiUtil.submit(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        List<IOTAddress> rootDeviceList = null;

                        {
                            rootDeviceList = UdpBroadcastUtil.discoverIOTDevices();
                        }
                       // log.error("11111111111rootDeviceList=" + rootDeviceList.toString());
                       // if(rootDeviceList.size()>0)
                        rootDeviceSet.addAll(rootDeviceList);
                       // log.error("2222222222rootDeviceList=" + rootDeviceSet.toString());
                    }
                });
                _futureList.add(_future);
                if (i < UDP_RETRY_TIME - 1)
                {
                    try
                    {
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
            for (Future<?> future : _futureList)
            {
                try
                {
                	/* Waits if necessary for the computation to complete, 
                	 * and then retrieves its result.
                	 * 阻塞式读取方式  等待线程任务执行完成
                	 * */
                    future.get();
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                catch (ExecutionException e)
                {
                    e.printStackTrace();
                }
            }
        }
        //log.error("discoverIOTMeshDevices(): rootDeviceSet=" + rootDeviceSet);
        // check whether the device is find already
        //返回指定的ZigBee网关信息
        targetDeviceSet = extractIOTAddress(deviceBssid, rootDeviceSet);
        if (targetDeviceSet != null)
        {
            log.debug("discoverIOTMeshDevices(): targetDeviceSet=" + targetDeviceSet);
            return targetDeviceSet;
        }
        thiclent = new EspSocketClient();
        Iterator<IOTAddress> list = rootDeviceSet.iterator();//先迭代出来
        DataOutputStream out;
        Socket socket = null;
        DataInputStream in;
        boolean conneted=false;
        byte[] senddata = {(byte) 0xFE,(byte) 0xE7,0,0,0x02,0x09,0x0b};
        byte[] result;
        log.error("准备获取网关mac地址-----------list.hasNext()=" + list.hasNext());
        while(list.hasNext()){
        	
        	IOTAddress iotgate = (IOTAddress) list.next();
        	log.error("iotgate.getInetAddress().toString()=" + iotgate.getInetAddress().getHostName());
        	conneted = thiclent.connect(iotgate.getInetAddress().getHostName(), 1206);//iotgate.getInetAddress().getHostName()
//        	try {
//				socket = new Socket("192.168.0.103", 1206);
//				out = new DataOutputStream(socket.getOutputStream()); 
//				in = new DataInputStream(socket.getInputStream());
//				
//				out.write(senddata);
//				in.read(result);
//			} catch (UnknownHostException e1) {
//				// TODO Auto-generated catch block
//				e1.printStackTrace();
//			} catch (IOException e1) {
//				// TODO Auto-generated catch block
//				e1.printStackTrace();
//			}
//        	String str = "";
// 			for(int i=0; i< result.length; i++){
// 				str = str + " " + (byte)result[i];
// 			}
// 			log.error("result 长度:" + result.length + "-------" + str + "Hex="  + HEX.encodeHexString(result));
 			
        	if(conneted){
        		  try {
         	    	 //开始发送数据
         	    	String sendString=new String( senddata , "ISO-8859-1" );
         	    	thiclent.writeRequest(sendString);
         			//开始接受数据进行判断处理
         			String ret =null;
         			
         			ret=	thiclent.readResponse();

         			result = ret.getBytes("ISO-8859-1");
         			String str = "";
         			for(int i=0; i< result.length; i++){
         				str = str + " " + (byte)result[i];
         			}
         			//log.error("result 长度:" + result.length +"::"+ ret +"-------" + str + "Hex="  + HEX.encodeHexString(result));
         			if(result.length ==  15){
         				int count = result.length;
         				log.error("result[5]" + result[Frame_index.COMMD_TYPE.ordinal()] + "-------" + ResPonse_type.Gate_report_Mac.mCode);
         				if(result[Frame_index.COMMD_TYPE.ordinal()] == (byte)ResPonse_type.Gate_report_Mac.mCode
         				)
         				{
         					//从第7个开始为ZigBee的mac地址
         					
         					String isoString = HEX.encodeHexString(result).substring(12, 28);
         					log.error("result zigbeemac:" + isoString);
         					iotgate.setBSSID(isoString);
         					thiclent.close();
         					thiclent = null;
         					
         				}
         			}
         			
         		} catch (IOException e) {
         			// TODO Auto-generated catch block
         			e.printStackTrace();
         		}
        	}else{
        		log.error("连接网关失败-----------");
        	}
        }
        
        
        return rootDeviceSet;

    }
    
    /**
     * @see IOTAddress discover IOT devices in the same AP by UDP broadcast
     * 
     * @return the List of IOTAddress
     */
    public static List<IOTAddress> discoverIOTDevices()
    {
        Set<IOTAddress> iotAddressSet = discoverIOTMeshDevices(null);
        List<IOTAddress> iotAddressList = new ArrayList<IOTAddress>();
        iotAddressList.addAll(iotAddressSet);
        return iotAddressList;
    }
    
    /**
     * @see IOTAddress discover IOT device in the same AP by UDP broadcast or in the mesh net
     * @param deviceBssid the device's bssid
     * @return the IOTAddress
     */
    public static IOTAddress discoverIOTDevice(String deviceBssid)
    {
        Set<IOTAddress> iotAddressSet = discoverIOTMeshDevices(deviceBssid);
        if (iotAddressSet.size() > 0)
        {
            return iotAddressSet.iterator().next();
        }
        else
        {
            return null;
        }
    }
}
